﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing.Design;
using System.Web.UI;
using TA.DotNetUtils;
using TA.JSON;
using TA.Turbo.Design;
using TA.Win.Utils.Design;


namespace TA.Turbo.Controls
{
	/// <summary>
	/// Describes a scenario for form submission by referencing certain controls and control groups for client-server transfer.
	/// </summary>
	public class PostScenario : BaseControlReferenceGroup, IJsonSerializable, IAssignedClientId
	{
		#region PUBLIC
		#region INIT
		/// <summary>
		/// Ctor.
		/// </summary>
		public PostScenario()
		{
			this.HttpVerb = HttpVerb.Post;
			this.PostMode = PostMode.Ajax;
			this.PostUrl = "";
			this.CausesValidation = true;
			this._controlReferenceGroups = new MultiControlValue<ControlReferenceGroup>(this);
		}
		#endregion
		#region PROPS
		/// <summary>
		/// Gets the page-wide unique identifier for this scenario.
		/// </summary>
		public override string UniqueID
		{
			get
			{
				return TPage.GetOptimizedUniqueId(ref this._uniqueId, base.UniqueID, true, 'P');
			}
		}
		[DefaultValue("")]
		public string Variable
		{
			get
			{
				if (string.IsNullOrEmpty(this._variable))
					return this.ID;

				return this._variable;
			}
			set
			{
				this._variable = value;
			}
		}
		#region OPTIONS
		/// <summary>
		/// The URL of the page to post (action). Defaults to current page.
		/// </summary>
		[Category("Options")]
		[DefaultValue("")]
		public string PostUrl { get; set; }
		/// <summary>
		/// HTTP verb.
		/// </summary>
		[Category("Options")]
		[DefaultValue(HttpVerb.Post)]
		public HttpVerb HttpVerb { get; set; }
		/// <summary>
		/// Post mode.
		/// </summary>
		[Category("Options")]
		[DefaultValue(PostMode.Ajax)]
		public PostMode PostMode { get; set; }
		/// <summary>
		/// True to strip the hidden fields from the post; false otherwise. Usually used with GET.
		/// </summary>
		[Category("Options")]
		[DefaultValue(false)]
		public bool Clean { get; set; }
		#endregion
		#region VALIDATION
		/// <summary>
		/// Comma-separated list of validation groups (for designer).
		/// </summary>
		[Category("Validation")]
		[DefaultValue("")]
		public string ValidationGroups
		{
			get
			{
				return _validationGroups.Value;
			}
			set
			{
				_validationGroups.Value = value;
			}
		}
		/// <summary>
		/// List of validation groups (for code behind).
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Category("Validation")]
		[Editor(typeof(StringCollectionEditor<ValidationGroupOptionsProvider>), typeof(UITypeEditor))]
		public List<string> ValidationGroupList
		{
			get
			{
				return _validationGroups.ValueList;
			}
		}
		/// <summary>
		/// Causes validation to be performed on the page.
		/// </summary>
		[Category("Validation")]
		[DefaultValue(true)]
		public bool CausesValidation { get; set; }
		#endregion
		#region TRANSFER
		/// <summary>
		/// If true, appends everything after the '#' in the address bar to the query string when sending a request to the server.
		/// </summary>
		[Category("Transfer")]
		[DefaultValue(false)]
		public bool SendHashString { get; set; }
		/// <summary>
		/// If true, outputs the hash string to the client after a successful request.
		/// </summary>
		[Category("Transfer")]
		[DefaultValue(false)]
		public bool ReturnHashString { get; set; }
		/// <summary>
		/// Comma-separated list of relative IDs of control reference groups to be used with this scenario.
		/// </summary>
		[Category("Transfer")]
		[DefaultValue("")]
		public string GroupIds
		{
			get
			{
				return _controlReferenceGroups.Value;
			}
			set
			{
				_controlReferenceGroups.Value = value;
			}
		}
		/// <summary>
		/// List of relative IDs of control reference groups to be used with this scenario.
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Category("Transfer")]
		[Editor(typeof(ControlIdCollectionEditor<ControlReferenceGroupIdLister>), typeof(UITypeEditor))]
		public List<string> GroupIdList
		{
			get
			{
				return _controlReferenceGroups.ValueList;
			}
		}
		/// <summary>
		/// List of control reference groups to be used with this scenario.
		/// </summary>
		[Browsable(false)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public List<ControlReferenceGroup> GroupList
		{
			get
			{
				return _controlReferenceGroups.ControlList;
			}
		}
		#endregion
		#region EVENTS
		/// <summary>
		/// Client script function/block executed before sending AJAX request.
		/// </summary>
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		[Editor(typeof(PropertyEditor), typeof(UITypeEditor))]
		[Category("Events")]
		public SimpleClientEvent Posting { get { return _posting; } }
		/// <summary>
		/// Client script function/block executed after AJAX request sent.
		/// </summary>
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		[Editor(typeof(PropertyEditor), typeof(UITypeEditor))]
		[Category("Events")]
		public SimpleClientEvent Posted { get { return _posted; } }
		/// <summary>
		/// Client script function/block executed after AJAX response received.
		/// </summary>
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		[Editor(typeof(PropertyEditor), typeof(UITypeEditor))]
		[Category("Events")]
		public SimpleClientEvent Response { get { return _response; } }
		/// <summary>
		/// Client script function/block executed after AJAX response received and processed.
		/// </summary>
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		[Editor(typeof(PropertyEditor), typeof(UITypeEditor))]
		[Category("Events")]
		public SimpleClientEvent ResponseProcessed { get { return _responseProcessed; } }
		/// <summary>
		/// Client script function/block executed after AJAX request errored out.
		/// </summary>
		[PersistenceMode(PersistenceMode.InnerProperty)]
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
		[Editor(typeof(PropertyEditor), typeof(UITypeEditor))]
		[Category("Events")]
		public SimpleClientEvent Error { get { return _error; } }
		#endregion
		/// <summary>
		/// Hidden property that stores IDs of all progress controls which should be activated when this scenario posts.
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[EditorBrowsable(EditorBrowsableState.Never)]
		[Browsable(false)]
		public string[] ProgressControls { get; set; }
		#endregion
		#region METHODS
		public string GetScript()
		{
			return string.Format("{0} = \"{1}\";", this.GetVariableName(), this.ClientID);
		}
		public virtual bool IsSame(PostScenario that)
		{
			if (that is CurrentPostScenario)
			{
				return ((CurrentPostScenario)that).Original == this;
			}
			return this == that;
		}
		public override string ToString()
		{
			return string.Format("{0};  P c:{1}, i:{2};  R c:{3}, i:{4};  G:{5}",
				this.ClientID,
				this.PostControls.Count, this.PostControlIdList.Count,
				this.ReturnControls.Count, this.ReturnControlIdList.Count,
				this.GroupList.Count);
		}
		/// <summary>
		/// Checks if the specified GlobalControlReference is part of this scenario.
		/// </summary>
		/// <param name="gcr"></param>
		/// <returns></returns>
		public bool IsApplicable(GlobalControlReference gcr)
		{
			return gcr.ScenariosMode.IsIncluded(gcr.ScenarioList, ps => this.IsSame(ps));
		}
		/// <summary>
		/// Merges the specified scenario into this scenario.
		/// </summary>
		/// <param name="scenario">The scenario to merge into this scenario.</param>
		public void Ensure(PostScenario scenario)
		{
			if (this.TPage != null)
			{
				if (scenario.Page == null)
					this.TPage.SpecialContainer.Controls.Add(scenario);

				Ensure_CRs(scenario.PostControls, this.PostControls);
				Ensure_CRs(scenario.ReturnControls, this.ReturnControls);

				Ensure_CRGs(scenario);
			}
		}
		/// <summary>
		/// Ensures that the ControlReferenceGroup is part of this scenario.
		/// </summary>
		/// <param name="crg"></param>
		public void Ensure(ControlReferenceGroup crg)
		{
			if (!this.GroupList.Contains(crg))
				this.GroupList.Add(crg);
		}
		/// <summary>
		/// Ensures that the specified control reference is part of this scenario's post control collection.
		/// </summary>
		/// <param name="cr"></param>
		public void EnsurePostControl(ControlReference cr)
		{
			Ensure_CR(cr, this.PostControls);
		}
		/// <summary>
		/// Ensures that the specified control reference is part of this scenario's return control collection.
		/// </summary>
		/// <param name="cr"></param>
		public void EnsureReturnControl(ControlReference cr)
		{
			Ensure_CR(cr, this.ReturnControls);
		}
		/// <summary>
		/// Ensures that the specified <see cref="UpdatePanel"/> is part of this scenario's redrawn update panel collection.
		/// </summary>
		/// <param name="up"></param>
		public void Ensure(UpdatePanel up)
		{
			if (!this.UpdatePanelList.Contains(up))
			{
				this.UpdatePanelList.Add(up);
			}
		}
		/// <summary>
		/// Gets the list of all <see cref="UpdatePanel"/>s which are to be updated as part of this scenario.
		/// </summary>
		/// <returns></returns>
		public List<UpdatePanel> GetAllUpdatePanels()
		{
			List<UpdatePanel> res = new List<UpdatePanel>();
			foreach (UpdatePanel up in this.UpdatePanelList)
				AddUpdatePanel(res, up);
			foreach (ControlReferenceGroup crg in this.GroupList)
			{
				foreach (UpdatePanel up in crg.UpdatePanelList)
					AddUpdatePanel(res, up);
			}

			return res;
		}
		/// <summary>
		/// List of all control references to be sent to the server as part of this scenario.
		/// </summary>
		public List<BaseControlReference> GetAllPostControls()
		{
			return GetAllControlReferences(this.GetPostControls(), ControlTransferDirection.Post,
						crg => crg.GetPostControls());
			//get
			//{
			//  if (_allPostControls == null)
			//    _allPostControls = GetAllControlReferences(this.GetPostControls(), ControlTransferDirection.Post,
			//      crg => crg.GetPostControls());
			//  return _allPostControls;
			//}
		}
		/// <summary>
		/// List of all control references to be returned to the client as part of this scenario.
		/// </summary>
		public Tuple<List<BaseControlReference>, List<UpdatePanel>> GetAllReturnControls()
		{
			List<BaseControlReference> ctls = GetAllControlReferences(this.GetReturnControls(), ControlTransferDirection.Return,
						crg => crg.GetReturnControls());
			List<UpdatePanel> upd = this.GetAllUpdatePanels();

			for (int i = 0; i < ctls.Count; ++i)
			{
				Control c = ctls[i].Control;
				while (c != null && !(c is UpdatePanel))
				{
					c = c.Parent;
				}
				if (c is UpdatePanel)
				{
					if (upd.Contains((UpdatePanel)c))
					{
						ctls.RemoveAt(i);
						--i;
					}
				}
			}

			return new Tuple<List<BaseControlReference>, List<UpdatePanel>>(ctls, upd);
			//get
			//{
			//  if (_allReturnControls == null)
			//    _allReturnControls = GetAllControlReferences(this.GetReturnControls(), ControlTransferDirection.Return,
			//      crg => crg.GetReturnControls());
			//  return _allReturnControls;
			//}
		}
		#endregion
		#endregion
		#region INTERNAL
		#region VARS
		MultiPartValue _validationGroups = new MultiPartValue();
		MultiControlValue<ControlReferenceGroup> _controlReferenceGroups;
		SimpleClientEvent _posting = new SimpleClientEvent()
			, _posted = new SimpleClientEvent()
			, _response = new SimpleClientEvent()
			, _responseProcessed = new SimpleClientEvent()
			, _error = new SimpleClientEvent();
		string _uniqueId;
		string _variable = "";
		//internal List<BaseControlReference> _allPostControls = null
		//  , _allReturnControls = null;
		#endregion
		#region INIT
		/// <summary>
		/// Copy ctor.
		/// </summary>
		/// <param name="source"></param>
		protected PostScenario(PostScenario source)
			: this()
		{
			TPage.Current.SpecialContainer.Controls.Add(this);

			this.PostUrl = source.PostUrl;
			this.HttpVerb = source.HttpVerb;
			this.PostMode = source.PostMode;
			this.ValidationGroups = source.ValidationGroups;
			this.SendHashString = source.SendHashString;
			this.ReturnHashString = source.ReturnHashString;
			this.CausesValidation = source.CausesValidation;
			this.Clean = source.Clean;

			this.Posting.Client = source.PrepareClientScript(source.Posting.Client);
			this.Posted.Client = source.PrepareClientScript(source.Posted.Client);
			this.Response.Client = source.PrepareClientScript(source.Response.Client);
			this.ResponseProcessed.Client = source.PrepareClientScript(source.ResponseProcessed.Client);
			this.Error.Client = source.PrepareClientScript(source.Error.Client);

			Copy(source.PostControls, this.PostControls);
			Copy(source.ReturnControls, this.ReturnControls);

			foreach (ControlReferenceGroup crg in source.GroupList)
			{
				this.GroupIdList.Add(crg.UniqueID);
			}
			foreach (UpdatePanel up in source.UpdatePanelList)
			{
				this.UpdatePanelList.Add(up);
			}
			this.Copy(source, source.PostControlIdList, this.PostControlIdList);
			this.Copy(source, source.ReturnControlIdList, this.ReturnControlIdList);
		}
		#endregion
		#region METHODS
		#region INIT
		private void Copy(Control startingPoint, List<string> source, List<string> dest)
		{
			foreach (string id in source)
			{
				dest.Add(startingPoint.FindControl(id).UniqueID);
			}
		}
		protected void Copy(ControlCollection<ControlReference> source, ControlCollection<ControlReference> dest)
		{
			foreach (ControlReference cr in source)
			{
				ControlReference cr2 = new ControlReference(cr.Control)
				{
					Value = cr.Value,
					Properties = cr.Properties,
					PropertiesMode = cr.PropertiesMode,
					SendAs = cr.SendAs,
				};
				dest.Add(cr2);
			}
		}
		/// <summary>
		/// Init. Registers <see cref="Script.PostScenario"/>.
		/// </summary>
		/// <param name="e"></param>
		protected override void OnInit(EventArgs e)
		{
			base.OnInit(e);

			if (this.TPage != null)
			{
				this.TPage.Includes.Add(Script.Turbo);

				if (this.PostMode == PostMode.Ajax)
				{
					this.TPage.Includes.Add(Script.TurboAjax);
				}
				else
				{
					this.TPage.Includes.Add(Script.TurboPage);
				}
				this.TPage.Includes.Add(Script.PostScenario);

				if (this.ReturnControlIdList.Count > 0 || this.ReturnControls.Count > 0)
				{
					this.TPage.Includes.Add(Script.ResponseControl);
				}
				//if (this.GetAllUpdatePanels().Count > 0)
				//{
				//  this.TPage.Includes.Add(Script.ResponseUpdatePanel);
				//}
			}
		}
		#endregion
		#region ENSURE CRs
		private void Ensure_CRs(ControlCollection<ControlReference> toEnsure, ControlCollection<ControlReference> crs)
		{
			foreach (ControlReference cr in toEnsure)
			{
				Ensure_CR(cr, crs);
			}
		}
		private void Ensure_CR(ControlReference cr, ControlCollection<ControlReference> crs)
		{
			BaseControlReference existing = null;
			foreach (BaseControlReference bcr in crs/*allCRs*/)
			{
				if (bcr.Control == cr.Control)
				{
					existing = bcr; break;
				}
			}
			if (existing == null)
			{
				existing = cr;
				crs.Add(cr);
			}
			else
			{
				existing.Ensure(cr);
			}
		}
		private void Ensure_CRGs(PostScenario scenario)
		{
			foreach (ControlReferenceGroup crgToEnsure in scenario.GroupList)
			{
				ControlReferenceGroup existing = null;
				foreach (ControlReferenceGroup cr in this.GroupList)
				{
					if (cr == crgToEnsure)
					{
						existing = cr;
						break;
					}
				}
				if (existing == null)
				{
					this.GroupList.Add(crgToEnsure);
					existing = crgToEnsure;
				}
				else
				{

				}
			}
		}
		#endregion
		#region GET ALL CRs
		BaseControlReference AddControlReference(Dictionary<string, BaseControlReference> all, List<BaseControlReference> listAll, BaseControlReference bcr)
		{
			if (bcr.Control == null) return null;

			if (all.ContainsKey(bcr.Control.UniqueID))
			{
				BaseControlReference existing = all[bcr.Control.UniqueID];

				existing.Ensure(bcr);
				return existing;
			}
			else
			{
				ControlReference cr = new ControlReference(bcr);
				all[bcr.Control.UniqueID] = cr;
				listAll.Add(cr);
				return cr;
			}
		}
		private void RemoveControlReference(Dictionary<string, BaseControlReference> all, List<BaseControlReference> listAll, BaseControlReference ctlRef)
		{
			all.Remove(ctlRef.Control.ClientID);
			listAll.Remove(ctlRef);
		}
		private List<BaseControlReference> GetAllControlReferences(IEnumerable<ControlReference> collection, ControlTransferDirection direction, Func<ControlReferenceGroup, IEnumerable<ControlReference>> getReferencesFromGroup)
		{
			//List<BaseControlReference> all = new List<BaseControlReference>();
			Dictionary<string, BaseControlReference> all = new Dictionary<string, BaseControlReference>();
			List<BaseControlReference> listAll = new List<BaseControlReference>();

			this.GetAllControlReferences_Add(all, listAll, collection, direction, getReferencesFromGroup);

			this.GetAllControlReferences_Process(all, listAll);

			return listAll;
		}
		private void GetAllControlReferences_Add(Dictionary<string, BaseControlReference> all, List<BaseControlReference> listAll, IEnumerable<ControlReference> collection, ControlTransferDirection direction, Func<ControlReferenceGroup, IEnumerable<ControlReference>> getReferencesFromGroup)
		{
			this.GetAllControlReferences_Add_Collection(all, listAll, collection);
			this.GetAllControlReferences_Add_GlobalControls(all, listAll, direction);
			this.GetAllControlReferences_Add_Groups(all, listAll, getReferencesFromGroup);
		}
		private void GetAllControlReferences_Add_Collection(Dictionary<string, BaseControlReference> all, List<BaseControlReference> listAll, IEnumerable<ControlReference> collection)
		{
			foreach (ControlReference cr in collection)
			{
				AddControlReference(all, listAll, cr);
				//all.Add(cr);
			}
		}
		private void GetAllControlReferences_Add_GlobalControls(Dictionary<string, BaseControlReference> all, List<BaseControlReference> listAll, ControlTransferDirection direction)
		{
			foreach (GlobalControlReference gcr in TPage.Current.AllGlobalControls)
			{
				if (this.IsApplicable(gcr) && (gcr.Direction & direction) == direction)
					AddControlReference(all, listAll, gcr);// all.Add(gcr);
			}
		}
		private void GetAllControlReferences_Add_Groups(Dictionary<string, BaseControlReference> all, List<BaseControlReference> listAll, Func<ControlReferenceGroup, IEnumerable<ControlReference>> getReferencesFromGroup)
		{
			foreach (ControlReferenceGroup crg in this.GroupList)
			{
				foreach (ControlReference cr in getReferencesFromGroup(crg))
					AddControlReference(all, listAll, cr); //all.Add(cr);
			}
		}
		private void GetAllControlReferences_Process(Dictionary<string, BaseControlReference> all, List<BaseControlReference> listAll)
		{
			for (int iCtlRef = 0; iCtlRef < listAll.Count; ++iCtlRef)
			//foreach (BaseControlReference ctlRef in all.Values)
			{
				BaseControlReference ctlRef = listAll[iCtlRef];

				this.GetAllControlReferences_Process_One_Expand(all, listAll, ctlRef);
				this.GetAllControlReferences_Process_One_CleanUp(all, listAll, ctlRef, ref iCtlRef);
			}
		}
		private void GetAllControlReferences_Process_One_Expand(Dictionary<string, BaseControlReference> all, List<BaseControlReference> listAll, BaseControlReference ctlRef)
		{
			TControl bc = ctlRef.Control as TControl;
			IValueControlContainer vcc = ctlRef.Control as IValueControlContainer;

			if (vcc != null)
			{
				List<string> propertiesToTransfer = this.GetAllControlReferences_Process_One_Expand_GetProperties(ctlRef, bc);
				if (propertiesToTransfer.Count > 0)
				{
					this.GetAllControlReferences_Process_One_Expand_TransferProperties(all, listAll, ctlRef, vcc, propertiesToTransfer);
				}

				if (ctlRef.Value) // we need to transfer a value
				{
					this.GetAllControlReferences_Process_One_Expand_Value(all, listAll, ctlRef, vcc);
				}
			}
		}
		private List<string> GetAllControlReferences_Process_One_Expand_GetProperties(BaseControlReference ctlRef, TControl bc)
		{
			List<string> res = new List<string>(); // we might need to transfer some (or all) of the properties using "value children"

			if (bc == null) // if this is not a TControl
			{ // we can't transfer any properties for it
				res.AddRange(ctlRef.AllPropertiesList); // we add all to our list

				ctlRef.PropertiesMode = InclusionMode.OnlyIncluded; // and remove them from the reference
				ctlRef.PropertiesList.Clear();
			}
			else
			{
				string[] allProps = ctlRef.AllPropertiesList; // go thru all properties
				foreach (string prName in allProps)
				{
					if (!bc.ClientProperties.ContainsKey(prName)) // if referenced control doesn't support it
					{
						res.Add(prName); // add it to our list
						MultiPartValue.RemoveValue(ctlRef.PropertiesMode, ctlRef.PropertiesList, prName); // remove from the reference
					}
				}
			}
			return res;
		}
		private void GetAllControlReferences_Process_One_Expand_TransferProperties(Dictionary<string, BaseControlReference> all, List<BaseControlReference> listAll, BaseControlReference ctlRef, IValueControlContainer vcc, List<string> propertiesToTransfer)
		{
			foreach (BaseControlReference crProperties in vcc.PropertiesChildren)
			{
				BaseControlReference addedCrProperties = this.AddControlReference(all, listAll, crProperties);

				foreach (string prName in propertiesToTransfer)
				{
					MultiPartValue.AddValue(addedCrProperties.PropertiesMode, addedCrProperties.PropertiesList, prName);
				}
			}
		}
		private void GetAllControlReferences_Process_One_Expand_Value(Dictionary<string, BaseControlReference> all, List<BaseControlReference> listAll, BaseControlReference ctlRef, IValueControlContainer vcc)
		{
			foreach (BaseControlReference crValue in vcc.ValueChildren)
			{
				this.AddControlReference(all, listAll, crValue);
			}
			ctlRef.Value = false;
		}
		private void GetAllControlReferences_Process_One_CleanUp(Dictionary<string, BaseControlReference> all, List<BaseControlReference> listAll, BaseControlReference ctlRef, ref int iCtlRef)
		{
			if (!ctlRef.Anything)
			{
				RemoveControlReference(all, listAll, ctlRef);
				--iCtlRef;
			}
		}
		#endregion
		#region UPDATE PANELS
		private void AddUpdatePanel(List<UpdatePanel> res, UpdatePanel updatePanel)
		{
			List<UpdatePanel> toRemove = new List<UpdatePanel>();
			foreach (UpdatePanel up in res)
			{
				if (up == updatePanel || up.IsAscendantOf(updatePanel))
					return;
				if (updatePanel.IsAscendantOf(up))
					toRemove.Add(up);
			}
			foreach (UpdatePanel up in toRemove)
				res.Remove(up);
			res.Add(updatePanel);
		}
		#endregion
		#region SCRIPT
		/// <summary>
		/// Returns the complete variable name to which this event's ID needs to be assigned.
		/// </summary>
		/// <returns></returns>
		protected string GetVariableName()
		{
			Manager m = this.Parent as Manager;
			if (m != null)
				return m.ClientScriptVariablePrefix + this.Variable;
			return this.Variable;
		}
		#endregion
		#region IJsonSerializable Members
		/// <summary>
		/// IsDirty.
		/// </summary>
		[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		[Browsable(false)]
		public bool IsDirty
		{
			get
			{
				return true;
			}
		}
		/// <summary>
		/// Serialize.
		/// </summary>
		/// <param name="jw"></param>
		public void Serialize(JsonWriter jw)
		{
			this.SerializeObject(jw);
		}
		/// <summary>
		/// SerializeContents.
		/// </summary>
		/// <param name="jw"></param>
		public void SerializeContents(JsonWriter jw)
		{
			jw.PropertyName("I");
			jw.Value(this.ClientID);

			// PROPERTIES
			if (!string.IsNullOrEmpty(this.PostUrl))
			{
				jw.PropertyName("P");
				jw.Value(this.ResolveUrl(this.PostUrl));
			}

			if (this.HttpVerb != HttpVerb.Post)
			{
				jw.PropertyName("V");
				jw.Value(this.HttpVerb);
			}

			if (this.PostMode != PostMode.Ajax)
			{
				jw.PropertyName("M");
				jw.Value(this.PostMode);
			}

			if (this.SendHashString)
			{
				jw.PropertyName("H");
				jw.Value(this.SendHashString);
			}

			if (!this.CausesValidation)
			{
				jw.PropertyName("C");
				jw.Value(this.CausesValidation);
			}

			if (this.Clean)
			{
				jw.PropertyName("L");
				jw.Value(this.Clean);
			}
			//PrepareScenarios_Write(jw, "Id", scenario.ClientID);


			// EVENTS
			this.SerializeClientEvent(jw, this.Posting, "Pi");
			this.SerializeClientEvent(jw, this.Posted, "Pe");
			this.SerializeClientEvent(jw, this.Response, "R");
			this.SerializeClientEvent(jw, this.ResponseProcessed, "D");
			this.SerializeClientEvent(jw, this.Error, "O");

			// VALIDATION GROUPS
			if (this.ValidationGroupList.Count > 0)
			{
				jw.PropertyName("G");

				jw.StartArray();
				foreach (string vg in this.ValidationGroupList)
					jw.Value(vg);
				jw.EndArray();
			}

			// PROGRESS CONTROLS
			List<string> ProgressControls = new List<string>();
			foreach (TurboProgress tp in TPage.Current.ProgressControls)
			{
				if (MultiPartValue.IsIncluded(tp.InclusionMode, tp.ScenarioList, this))
					ProgressControls.Add(tp.ClientID);
			}
			if (ProgressControls.Count > 0)
			{
				jw.PropertyName("T");

				jw.StartArray();
				foreach (string pg in ProgressControls)
					jw.Value(pg);
				jw.EndArray();
			}

			// POST CONTROLS
			List<BaseControlReference> apc = this.GetAllPostControls();
			if (apc.Count > 0)
			{
				jw.PropertyName("W");
				jw.StartArray();

				foreach (BaseControlReference bcr in apc)
				{
					bcr.Serialize(jw);
				}

				jw.EndArray();
			}
		}

		private void SerializeClientEvent(JsonWriter jw, SimpleClientEvent clientEvent, string propName)
		{
			if (!string.IsNullOrEmpty(clientEvent.Client))
			{
				jw.PropertyName(propName);
				jw.Value(TPage.PrepareScript(this.PrepareClientScript(clientEvent.Client)));
			}
		}

		protected string PrepareClientScript(string script)
		{
			if (!string.IsNullOrEmpty(script) && !script.Contains("(") && this.Parent is Manager)
			{
				script = ((Manager)this.Parent).ClientScriptVariablePrefix + script;
			}
			return script;
		}

		#endregion
		#endregion
		#endregion
	}
}